15장 let, const 키워드와 블록 레벨 스코프
15-1. var 키워드로 선언한 변수의 문제점
15-1-1. 변수 중복 선언 허용
var 키워드로 선언한 변수는 중복 선언이 가능
var x = 1;
var y = 1;
// var 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용
// 초기화문이 있는 변수 선언문은 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 동작
var x = 100;
// 초기화문이 없는 변수 선언문은 무시
var y;
console.log(x); // 100
console.log(y); // 1- 위 예제의 var 키워드로 선언한 x, y 변수는 중복 선언됨
- 초기화문 유무에 따라 다르게 동작
- 초기화문 있음: var 키워드가 없는 것처럼 동작
- 초기화문 없음: 무시됨
- 에러가 발생하지 않음: 먼저 선언된 변수 값이 의도치 않게 변경되는 부작용 발생 가능
15-1-2. 함수 레벨 스코프
함수 외부에서 var 키워드로 선언한 변수는 코드 블록 내에서 선언해도 전역 변수가 됨
var i = 10;
// for 문에서 선언한 i는 전역 변수
for (var i = 0; i < 5; i++){
console.log(i); // 0 1 2 3 4
}
// 의도치 않게 i 변수 값 변경
console.log(i); // 5함수 레벨 스코프는 전역 변수를 남발할 가능성을 높임!
15-1-3. 변수 호이스팅
var 키워드로 변수를 선언하면 변수 호이스팅에 의해 선언문이 선두로 끌어올려진 것처럼 동작 -> 즉, 변수 호이스팅에 의해 var 키워드로 선언한 변수는 변수 선언문 이전에 참조 가능
// 이 시점에는 변수 호이스팅에 의해 이미 foo 변수가 선언됨(1. 선언 단계)
// 변수 foo는 undefined로 초기화(2. 초기화 단계)
console.log(foo); // undefined
// 변수에 값을 할당(3. 할당 단계)
foo = 123;
console.log(foo); // 123
// 변수 선언은 런타임 이전에 암묵적 실행
var foo;변수 선언문 이전에 변수를 참조하는 것은 에러가 발생하진 않음 다만, 프로그램 흐름 상 맞지 않고, 가독성 떨어지며 오류 발생 여지를 남김!
15-2. let 키워드
var를 보완하여 ES6에서 새로 도입된 변수 선언 키워드
15-2-1. 변수 중복 선언 금지
let 키워드로 이름이 같은 변수를 중복 선언 시, 문법 에러(SyntaxError) 발생
var foo = 123;
var foo = 456;
let bar = 123;
let bar = 456; // SyntaxError: Identifier 'bar' has already been declared15-2-2. 블록 레벨 스코프
let 키워드로 선언한 변수는 함수 레벨 스코프 var 키워드 변수와는 달리 모든 코드 블록을 지역 스코프로 인정
let foo = 1; // 전역 변수
{
let foo = 2; // 지역 변수
let bar = 3; // 지역 변수
}
console.log(foo); // 1
console.log(bar); // ReferenceError: bar is not defined- 함수 내의 코드 블록은 함수 레벨 스코프에 중첩됨

15-2-3. 변수 호이스팅
let 키워드로 선언한 변수는 var 키워드 변수와는 달리 변수 호이스팅이 발생하지 않는 것처럼 동작
console.log(foo); // ReferenceError: foo is not defined
let foo;- 변수 선언문 이전에 let 키워드 변수를 참조하면 참조 에러(ReferenceError)가 발생
var 키워드 변수
- var 키워드 변수는 런타임 이전에 자바스크립트 엔진에 의해 암묵적으로 “선언 단계”와 “초기화 단계”가 한 번에 진행됨
동작 순서
- 선언 단계에서 스코프(실행 컨텍스트의 렉시컬 환경)에 변수 식별자를 등록
- 초기화 단계에서 undefined로 변수를 초기화 -> 따라서 변수 선언문 이전에 변수에 접근해도 스코프에 변수가 존재하여 에러 발생하지 않음
// 초기화 단계
console.log(foo); // undefined
var foo;
console.log(foo); // undefined
foo = 1; // 할당 단계
console.log(foo); // 1
let 키워드 변수
- let 키워드 변수는 “선언 단계”와 “초기화 단계”가 분리되어 진행
동작 순서
- 런타임 이전에 자바스크립트에 의해 암묵적으로 선언 단계가 먼저 실행
- 하지만 초기화 단계는 변수 선언문에 도달했을 때 실행 -> 만약 초기화 단계가 실행되기 이전에 변수에 접근하려 하면 참조 에러(ReferenceError) 발생
일시적 사각지대(TDZ): 스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간
console.log(foo); // ReferenceError: foo is not defined
let foo; // 초기화 단계
console.log(foo); // undefined
foo = 1; // 할당 단계
console.log(foo); // 1
let 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는 것처럼 보임 -> 하지만 아님!
let foo = 1; // 전역 변수
{
console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
let foo = 2; // 지역 변수
}- 만약 호이스팅이 발생하지 않으면 전역 변수 foo의 값을 출력해야 함
- 호이스팅이 발생했기 때문에 참조 에러(ReferenceError)가 발생!
결론: 자바스크립트는 모든 선언을 호이스팅함 단, ES6에서 도입된 let, const, class를 사용한 선언문은 이가 발생하지 않는 것처럼 동작
15-2-4. 전역 객체와 let
전역 객체 window의 프로퍼티가 되는 경우
- var 키워드로 선언한 변수
- 전역 함수
- 선언하지 않은 변수에 값을 할당한 암묵적 전역
let 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아님!! 즉, window.foo와 같이 접근할 수 없음
let x = 1;
console.log(window.x); // undefined
console.log(x); // 1let 전역 변수는 보이지 않는 개념적 블록(전역 렉시컬 환경의 선언적 환경 레코드) 내에 존재 이에 대해서는 23장 “실행 컨텍스트”에서 자세히 살펴보자
15-3. const 키워드
const 키워드는 상수를 선언하기 위해 사용 하지만 반드시 상수만을 위한 건 아님
15-3-1. 선언과 초기화
const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 함 그렇지 않으면 문법 에러(SyntaxError) 발생
const foo = 1;
const foo; // SyntaxError: Missing initializer in const declarationconst 키워드 변수는 let과 마찬가지로 블록 레벨 스코프를 가짐
또한, 변수 호이스팅이 발생하지 않는 것처럼 동작
{
// 변수 호이스팅이 발생하지 않는 것처럼 동작
console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
const foo = 1;
console.log(foo); // 1
}
// 블록 레벨 스코프
console.log(foo); // ReferenceError: foo is not defined15-3-2. 재할당 금지
재할당이 자유로운 var 또는 let 키워드 변수와 달리 const 키워드로 선언한 변수는 재할당이 금지
const foo = 1;
foo = 2; // TypeError: Assignment to constant variable15-3-3. 상수
: 재할당이 금지된 변수
const 키워드로 선언한 변수에 원시 값을 할당한 경우 변수 값을 변경할 수 없음
상수는 상태 유지와 가독성, 유지보수의 편의를 위해 적극적으로 사용해야 함
- 상수를 활용하지 않은 안좋은 예시
// 세전 가격
let preTaxPrice = 100;
// 세후 가격
let afterTaxPrice = preTaxPrice + (preTaxPrice * 0.1);
console.log(afterTaxPrice); // 110- 코드 내에서 사용한 0.1은 어떤 의미로 사용했는지 알기 어려움
- 세율을 의미하는 0.1은 쉽게 바뀌지 않는 값, 프로그램 전체에서 고정된 값 -> 따라서 이때 세율을 상수로 정의하면 좋음!
일반적으로 상수의 이름은 대문자로 선언해 상수임을 명확히 나타냄 여러 단어로 이뤄진 경우에는 언더스코어(_)로 구분하여 스네이크 케이스로 표현
- 상수를 활용한 좋은 예시
const TAX_RATE = 0.1;
// 세전 가격
let preTaxPrice = 100;
// 세후 가격
let afterTaxPrice = preTaxPrice + (preTaxPrice * TAX_RATE);
console.log(afterTaxPrice); // 11015-3-4. const 키워드와 객체
const 키워드 변수에 원시 값을 할당한 경우 값을 변경할 수 없음
단, const 키워드로 선언된 변수에 객체를 할당한 경우 값을 변경할 수 있음
const person = {
name: 'Lee'
};
person.name = 'Kim';
console.log(person); // {name: "Kim"}- const 키워드는 재할당을 금지할 뿐 “불변”을 의미하지는 않음 -> 따라서, 재할당은 불가능하지만 프로퍼티 동적 생성, 삭제, 프로퍼티 값 변경 등으로 객체를 변경하는 건 가능 단, 이때 객체가 변경되더라도 변수에 할당된 참조 값은 변경되지 않음
필자 정리
- const 키워드 변수는 메모리 주소의 어떤 값을 가리키고 있음
- 이때 메모리 주소의 값이나 그 안의 참조 값을 변경할 수 없음
- 단, 이 참조 값이 포인터처럼 객체를 가리키고 있다면 객체의 프로퍼티 값은 const의 영향을 받지 않음!!
15-4. var vs. let vs. const
| 키워드 | 권장 사용법 |
|---|---|
| var | ES6에선 사용하지 않음 |
| const | 기본 변수 선언 |
| let | 재할당 필요한 경우 한정 |
- 변수를 선언하는 시점에는 재할당이 필요할지 잘 모르는 경우 많음
- 또한 객체는 의외로 재할당하는 경우가 드물음!
🧐 var, let, const 개념 확인 문제
- var 키워드가 가진 문제점에 대한 설명으로 옳지 않은 것은 무엇인가요?
A. 변수 중복 선언을 허용하여 초기화문이 있는 경우 기존 변수의 값을 의도치 않게 변경할 수 있다.
B. 함수 레벨 스코프를 가져서, 함수 외부의 for, if 등의 코드 블록 내에서 선언해도 전역 변수가 될 가능성이 높다.
C. 변수 선언문 이전에 변수를 참조해도 에러가 발생하지 않아 프로그램 흐름에 맞지 않고 가독성을 떨어뜨린다.
D. 선언 단계와 초기화 단계가 분리되어 진행되어, 스코프의 시작 지점부터 초기화 지점까지 일시적 사각지대(TDZ)가 발생한다.
정답 및 해설 보기
정답: D
해설: D는 let 또는 const 키워드 변수의 특징입니다. var 키워드 변수는 런타임 이전에 “선언 단계”와 “초기화 단계”가 한 번에 진행되어 undefined로 초기화되므로 TDZ가 발생하지 않습니다.
- 다음 코드에 대한 설명으로 가장 적절한 것은 무엇인가요?
let foo = 1; // 전역 변수
{
let foo = 2; // 지역 변수
let bar = 3; // 지역 변수
}
console.log(foo);
console.log(bar); A. 두 console.log 모두 ReferenceError가 발생한다.
B. let 변수는 함수 레벨 스코프를 따르므로 console.log(foo)는 2를 출력한다.
C. console.log(foo)는 1을 출력하고, console.log(bar)는 ReferenceError를 발생시킨다.
D. foo와 bar 모두 전역 변수이므로 1과 3을 각각 출력한다.
정답 및 해설 보기
정답: C
해설: let 키워드는 블록 레벨 스코프를 지원합니다. 중괄호 내의 foo와 bar는 지역 스코프에 갇히므로, 블록 외부의 foo는 전역 변수인 1을 출력하고, 블록 내부의 bar는 블록 외부에서 참조할 수 없어 ReferenceError가 발생합니다.
- let 키워드가 var 키워드와 달리 변수 호이스팅이 발생하지 않는 것처럼 동작하는 이유로 가장 적절한 것은 무엇인가요?
A. let 변수는 선언 단계 자체가 런타임 이후에 실행되기 때문이다.
B. 선언 단계와 초기화 단계가 분리되어, 초기화 단계 이전에 변수에 접근하면 참조 에러가 발생하기 때문이다.
C. let 변수는 전역 객체의 프로퍼티로 등록되지 않기 때문이다.
D. let 변수는 블록 레벨 스코프를 가져서 코드 블록의 선두로 호이스팅되지 않기 때문이다.
정답 및 해설 보기
정답: B
해설: 자바스크립트는 모든 선언을 호이스팅하지만, let 변수는 “선언 단계”만 런타임 이전에 실행되고 “초기화 단계”는 선언문에 도달했을 때 실행됩니다. 이 초기화 이전에 변수에 접근하려는 시도는 **일시적 사각지대(TDZ)**에 해당하여 ReferenceError를 발생시킵니다.
- const 키워드에 대한 설명으로 옳은 것은 무엇인가요?
A. const로 선언한 변수는 재할당이 금지되므로, 원시 값뿐만 아니라 객체나 배열도 불변(Immutable) 상태로 유지된다. B. const 키워드로 선언한 변수는 let 키워드와 달리 변수 호이스팅이 전혀 발생하지 않는다. C. const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 하며, 그렇지 않으면 문법 에러가 발생한다. D. 상수를 선언할 때만 사용해야 하며, 재할당이 필요한 변수를 선언할 때도 적극 권장된다.
정답 및 해설 보기
정답: C
해설: const 키워드 변수는 재할당이 금지되므로, 선언과 동시에 초기화하지 않으면 사용할 수 없어 문법 에러가 발생합니다.
A. const는 재할당을 금지할 뿐, 객체의 내부 프로퍼티 변경은 가능하므로 불변을 의미하지 않습니다.
B. const는 let과 마찬가지로 호이스팅은 발생하지만, TDZ 때문에 발생하지 않는 것처럼 동작합니다.
D. const는 상수를 선언할 때 사용하며, 기본 변수 선언 시 권장되지만, 재할당이 필요한 경우는 let을 사용해야 합니다.
- const 키워드 변수에 객체를 할당한 다음 코드에 대한 설명으로 가장 정확한 것은 무엇인가요?
const person = {
name: 'Lee'
};
person.name = 'Kim'; // (가)
person = { name: 'Park' }; // (나)A. (가)에서 객체의 프로퍼티가 변경되었으므로, const가 재할당을 막지 못해 메모리 누수가 발생한다.
B. (가)는 허용되지만 (나)는 TypeError가 발생한다. 이는 const가 객체 내부 값의 변경은 허용하지만 **변수에 할당된 참조 값의 변경(재할당)**은 금지하기 때문이다.
C. const는 불변(Immutable)을 의미하므로 (가)와 (나) 모두 TypeError가 발생한다.
D. var 키워드처럼 동작하므로 (가)와 (나) 모두 허용된다.
정답 및 해설 보기
정답: B
해설: const는 재할당을 금지할 뿐, 변수가 가리키는 객체의 내부 프로퍼티 변경은 막지 않습니다. 따라서 프로퍼티 값 변경인 (가)는 가능하지만, 변수에 새로운 객체의 참조 값을 할당하려는 (나)는 TypeError가 발생합니다.